﻿using System;
using System.Drawing;
using System.IO;
using System.Threading.Tasks;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using OpenCvSharp;

namespace HuaWeiCamera.Class
{
    /// <summary>
    /// 回调为YUV图像时，可以通过此类保存为图片
    /// </summary>
    public static class YuvHelper
    {
        private static readonly double[,] Yuv2RgbConvertMatrix = { { 1, 0, 1.4022 }, { 1, -0.3456, -0.7145 }, { 1, 1.771, 0 } };

        #region 将一桢 YUV 格式的图像转换为一桢 RGB 格式图像。
        /// <summary>
        /// 将一桢 YUV 格式的图像转换为一桢 RGB 格式图像。
        /// </summary>
        /// <param name="yuvFrame">YUV 格式图像数据。</param>
        /// <param name="rgbFrame">RGB 格式图像数据。</param>
        /// <param name="width">图像宽（单位：像素）。</param>
        /// <param name="height">图像高（单位：像素）。</param>
        public static void ConvertYuv2Rgb(byte[] yuvFrame, int width, int height, ref byte[] rgbFrame)
        {
            int uIndex = width * height;
            int vIndex = uIndex + ((width * height) >> 2);
            int gIndex = width * height;
            int bIndex = gIndex * 2;

            int temp = 0;

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    // R分量
                    temp = (int)(yuvFrame[y * width + x] + (yuvFrame[vIndex + (y / 2) * (width / 2) + x / 2] - 128) * Yuv2RgbConvertMatrix[0, 2]);
                    rgbFrame[y * width + x] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));

                    // G分量
                    temp = (int)(yuvFrame[y * width + x] + (yuvFrame[uIndex + (y / 2) * (width / 2) + x / 2] - 128) * Yuv2RgbConvertMatrix[1, 1] + (yuvFrame[vIndex + (y / 2) * (width / 2) + x / 2] - 128) * Yuv2RgbConvertMatrix[1, 2]);
                    rgbFrame[gIndex + y * width + x] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));

                    // B分量
                    temp = (int)(yuvFrame[y * width + x] + (yuvFrame[uIndex + (y / 2) * (width / 2) + x / 2] - 128) * Yuv2RgbConvertMatrix[2, 1]);
                    rgbFrame[bIndex + y * width + x] = (byte)(temp < 0 ? 0 : (temp > 255 ? 255 : temp));
                }
            }
        }
        #endregion

        #region 将转换后的 RGB 图像数据按照 BMP 格式写入文件。
        /// <summary>
        /// 将转换后的 RGB 图像数据按照 BMP 格式写入文件。
        /// </summary>
        /// <param name="rgbFrame">RGB 格式图像数据。</param>
        /// <param name="width">图像宽（单位：像素）。</param>
        /// <param name="height">图像高（单位：像素）。</param>
        /// <param name="bmpFile"> BMP 文件名。</param>
        public static void WriteBmp(byte[] rgbFrame, int width, int height, string bmpFile)
        {
            // 写 BMP 图像文件。
            int yu = width * 3 % 4;
            int bytePerLine = 0;
            yu = yu != 0 ? 4 - yu : yu;
            bytePerLine = width * 3 + yu;

            using (FileStream fs = File.Open(bmpFile, FileMode.Create))
            {
                using (BinaryWriter bw = new BinaryWriter(fs))
                {
                    bw.Write('B');
                    bw.Write('M');
                    bw.Write(bytePerLine * height + 54);
                    bw.Write(0);
                    bw.Write(54);
                    bw.Write(40);
                    bw.Write(width);
                    bw.Write(height);
                    bw.Write((ushort)1);
                    bw.Write((ushort)24);
                    bw.Write(0);
                    bw.Write(bytePerLine * height);
                    bw.Write(0);
                    bw.Write(0);
                    bw.Write(0);
                    bw.Write(0);

                    byte[] data = new byte[bytePerLine * height];
                    int gIndex = width * height;
                    int bIndex = gIndex * 2;

                    for (int y = height - 1, j = 0; y >= 0; y--, j++)
                    {
                        for (int x = 0, i = 0; x < width; x++)
                        {
                            data[y * bytePerLine + i++] = rgbFrame[bIndex + j * width + x];    // B
                            data[y * bytePerLine + i++] = rgbFrame[gIndex + j * width + x];    // G
                            data[y * bytePerLine + i++] = rgbFrame[j * width + x];  // R
                        }
                    }

                    bw.Write(data, 0, data.Length);
                    bw.Flush();
                }
            }
        }
        #endregion

        #region Mat转BitMapImage
        /// <summary>
        /// Mat转BitMapImage
        /// Mat image=new Mat(FileNames[i],ImreadModes.Unchanged); 
        /// </summary>
        /// <param name="image">Mat</param>
        /// <returns></returns>
        public static BitmapImage MatToBitmapImage(Mat image)
        {

            Bitmap bitmap = OpenCvSharp.Extensions.BitmapConverter.ToBitmap(image);
            using (MemoryStream stream = new MemoryStream())
            {
                bitmap.Save(stream, System.Drawing.Imaging.ImageFormat.Png);

                stream.Position = 0;
                BitmapImage result = new BitmapImage();
                result.BeginInit();
                // According to MSDN, "The default OnDemand cache option retains access to the stream until the image is needed."
                // Force the bitmap to load right now so we can dispose the stream.
                result.CacheOption = BitmapCacheOption.OnLoad;
                result.StreamSource = stream;
                result.EndInit();
                result.Freeze();
                return result;
            }
        }
        #endregion

        #region 保存WriteableBitmap图片
        /// <summary>
        /// 保存WriteableBitmap图片
        /// </summary>
        /// <param name="bitmap">WriteableBitmap</param>
        /// <param name="filePath">要保存为的图片全路径</param>
        public static void SavePic(WriteableBitmap bitmap, string filePath)
        {
            Task.Run(() =>
            {
                var bitmapEncoder = new JpegBitmapEncoder();
                bitmapEncoder.Frames.Add(BitmapFrame.Create(bitmap));
                bitmapEncoder.Save(File.OpenWrite(filePath));
            });
        }

        #endregion

        #region byte[]存图片
        /// <summary>
        /// 把Byte[]保存为图片
        /// </summary>
        /// <param name="imgBytes">图片数组</param>
        /// <param name="filePath">文件地址</param>
        /// <returns></returns>
        public static async Task Byte2Jpg(byte[] imgBytes, string filePath)
        {
            if(imgBytes==null || imgBytes.Length==0 || string.IsNullOrWhiteSpace(filePath)) return;
            await Task.Run(async () =>
            {
                using (var stream = new FileStream(filePath, FileMode.Create))
                {
                    await stream.WriteAsync(imgBytes,0,imgBytes.Length);
                    await stream.FlushAsync();
                }
            }).ConfigureAwait(false);
        }
        

        #endregion
    }
}
